{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import triton \n",
    "import triton.language as tl\n",
    "from mla_triton import triton_mla\n",
    "torch.cuda.empty_cache()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# triton benchmark"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "def torch_mla(qk_merge, compress_kv, v_weight, q_rope, k_rope, scale, v_head_dim, attention_mask=None, golden=False):\n",
    "    if golden:\n",
    "        dtype = torch.float32\n",
    "        qk_merge = qk_merge.to(dtype)\n",
    "        compress_kv = compress_kv.to(dtype)\n",
    "        v_weight = v_weight.to(dtype)\n",
    "        q_rope = q_rope.to(dtype)\n",
    "        k_rope = k_rope.to(dtype)\n",
    "    device = k_rope.device\n",
    "    dtype = k_rope.dtype\n",
    "    q_len = qk_merge.shape[-2]\n",
    "    kv_len = k_rope.shape[-2]\n",
    "    min_value = torch.finfo(dtype).min\n",
    "    score1 = qk_merge @ compress_kv.unsqueeze(1).transpose(-1,-2)\n",
    "    score2 = q_rope @ k_rope.transpose(-1,-2)\n",
    "    if q_len > 1: # prefill\n",
    "        causal_mask:torch.tensor = torch.full((q_len, kv_len), min_value, dtype=dtype, device=device).triu(diagonal=1)\n",
    "        causal_mask = causal_mask[None, None, :, :].expand(q_rope.shape[0], -1, -1, -1)\n",
    "        if attention_mask is not None:\n",
    "            causal_mask:torch.tensor = causal_mask.masked_fill((1-attention_mask[:, None, None, :]).bool(), min_value)\n",
    "    else: # decode\n",
    "        if attention_mask is None:\n",
    "            causal_mask = 0\n",
    "        else:\n",
    "            causal_mask = torch.zeros_like(attention_mask).masked_fill((1-attention_mask).bool(), min_value)\n",
    "            causal_mask = causal_mask[:, None, None, :]\n",
    "    score = (score1 + score2) * scale + causal_mask\n",
    "    attn_weight = torch.nn.functional.softmax(score, dim=-1, dtype=torch.float32).to(dtype)\n",
    "    o = attn_weight @ compress_kv.unsqueeze(1)\n",
    "    v_weight = v_weight.view(-1, v_head_dim, qk_merge.shape[-1]).unsqueeze(0) \n",
    "    o = o @ v_weight.transpose(-1, -2)\n",
    "    return o"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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NmbMmEGbNm0AaN++PSdOnODrr78mIyODadOm0bRpUyZPnswHH3wAwNy5c6lfvz4TJkwgOjqaiRMnOtvef/99unfvzqhRo2jdujWPPPIIs2fPrupuiYiImGL3id3cu+hexwSC7R7gH7H/MLskl1PlYScyMpJhw4YBkJeXx/Tp07njjjtISkoiNjaWwMBAANq0aUNycjIASUlJdO/eHYvFAkCnTp1ITEx0tt1yyy3O1z+/rSR2ux2bzVZkERERcUcZZzKImx/HqTOn6NywM2/3fdv5WSnnmHaCclJSEnXr1uWbb77htddew2azERV17g6sFosFq9VKenp6sbbg4GBSU1MBymwryZQpUwgJCXEukZHVczZJERFxbwWFBQz+bDC7TuyiYXBDPr/3c/y8/cwuyyWZFnbatGnDypUriY6OZtSoUXh7e+PnV/SP5O/vT3Z2drG2s+uBMttKMm7cODIyMpzLwYMHK7hnIiIile/J1U/yzZ5vCPAOYMngJdQNqmt2SS7LtLBjsVjo0KEDH330EZ9//jnh4eGkpaUVeU5mZia+vr7F2s6uB8psK4mfnx/BwcFFFhEREXfy0c8f8XLCywDMGjiL9vXam1yRa6vysLNhwwbi4+Odj319fbFYLLRs2ZKEhATn+pSUFOx2O+Hh4cTExBRp27p1Kw0aNAAos01ERMTTbD60mdFfjQZgfJfx3HPtPSZX5PqqPOw0b96cd999l3fffZeDBw/y1FNP0atXL/r06YPNZmPmzJkATJ48mR49emC1WomLi2PTpk2sXr2avLw8pk6dSu/ejsvqBg0axIIFC9i+fTtZWVm89tprzjYRERFPcsh2iIELBpJbkMvAawbybPdnzS7JLViM82fuqyKrVq3iscce4+DBg/Tu3Zs333yTq666iqVLlzJkyBACAgLw8vJi/fr1tGrVCoC3336bsWPHEhQURGhoKAkJCdSpUweAp59+mpdffhl/f3+io6PZuHEjAQEBl1SLzWYjJCSEjIwMHdISERGXlZ2XTdeZXUk8kkjr2q35/s/fE+QbZHZZpinP57cpYacsR48eJTExkdjYWGrVqlWkLSUlhZ07d9KlSxeCgor+gZOTkzl8+DDdunUr85ydCynsiIiIqzMMg6GfD2XBjgVEBEbw019+okloE7PLMpVbh52qprAjIiKubvLGyTy99mm8vbxZfd9q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wo6IiIh4NIUdD+Ln58czzzyDn5+f2aVUOvXV81SXfoL66omqSz/BPfta7U9QFhEREc+mkR0RERHxaAo7IiIi4tEUdkRERMSjKey4kSVLlnD11Vfj7e1Nu3bt+PXXXwEYO3YsFovFuTRr1sy5zY4dO4iJiSEsLIz4+PhLmlbbFZTWp7L6s2HDBlq2bElERATTpk0zq/RymTVrVpF+nl1mzZpFXFxckXU9evRwbudOfT1+/DhRUVHs27fPue5y/46LFi2icePG1K9fn/nz51dVFy5JSf0sbZ8F995vS+rr5fbH1d/LF/a1rH0WcNv9trT3qsfsq4a4hT179hhhYWHGJ598Yhw9etS4++67jRtvvNEwDMPo3LmzsWzZMiM9Pd1IT083bDabYRiGcebMGaNJkybGX//6V2PPnj1Gnz59jA8//NDMblyykvpUVn+OHTtmBAcHG88++6yxe/duo3379sbatWtN7sXF2e12Zx/T09ONgwcPGhEREcaePXuMevXqGdu3b3e2ZWVlGYbhXn1NS0szbrjhBgMwUlJSDMMo+31ZVt+2b99u+Pr6Gu+9956xbds2o1mzZsbOnTvN6loRJfWzrH3WMNx3vy2pr4Zxef1x9fdySX0ta581DMMt99vS3quetK8q7LiJL7/80njnnXecj9euXWsEBAQYeXl5RnBwsJGZmVlsmy+++MIICwszTp8+bRiGYfz888/GTTfdVGU1X67S+lRWf6ZPn25cc801RmFhoWEYhrF48WJj2LBhVVt4BXjhhReMv/zlL8ahQ4eMunXrlvgcd+rrrbfearz66qtFPiwu9+/46KOPGr1793a+9owZM4ynn366CntTupL6Wdo+axilv8cNw/X325L6ern9cfX3ckl9vdDZfdYwDLfdb0t7r3rSvqrDWG6iX79+jB492vl4165dREdHs337dgoLC2nXrh0BAQHcdtttHDhwAICkpCRiY2MJDAwEoE2bNiQnJ5tSf3mU1qey+pOUlET37t2d9zfr1KkTiYmJpvXhcpw5c4ZXX32Vp556ih9//JGCggIaNmxIjRo1GDx4MOnp6YB79fW9995j7NixRdZd7t8xKSmJW265xfk6rtTvkvpZ2j4Lpb/HwfX325L6ern9cfX3ckl9Pd/5+yzgtvttae9VT9pXFXbcUG5uLq+88gpjxowhOTmZFi1aMHv2bLZt24a3t7fzTWuz2YiKinJuZ7FYsFqtzp3PVZXWp7L6c2FbcHAwqampZpR/2ebNm8cNN9xAkyZN2LlzJ23btmXZsmVs3ryZlJQUxo0bBxT/u7pyX8+v86zL/Tu6cr9L6uf5zt9nofT3OLj+fltSXy+3P678N4WL/13P32cBj9hvz3+vetK+Wu1vBOqOnnnmGWrUqMGoUaPw8fFh2LBhzrY333yTqKgobDYb3t7exWa49Pf3Jzs7m7CwsKou+5INGzasxD61bNmy1P5c2Nez693J22+/zaRJkwAYN26c8x9JgJdeeok777yTt99+2+37Wtb7sqy+uXO/z99nofT3uLvut5fbH3f+m0LRfRY8Y789/706fvx4j9lXNbLjZtauXcsbb7zBvHnz8PHxKdZeu3ZtCgsLOXLkCOHh4aSlpRVpz8zMxNfXt6rKrRBn+1S3bt1S+3NhX92tn3v27GHPnj307NmzxPbatWtz4sQJ7Ha72/e1rPdlWX1z135fbJ8Fz9tvL7U/7vo3hYvvs+B+++2F71VP2lcVdtxISkoKQ4YM4Y033qBVq1YAxMfHM2/ePOdzEhIS8PLyIjIykpiYGBISEopsf3anc2Wl9al169al9ufCvm7dupUGDRpUad1X4tNPP6Vfv37OD8N7772X7777ztmekJBAnTp18PPzc/u+lvW+LKtv7tjvkvZZ8Lz99nL7445/07Mu3GfBvffbkt6rHrWvmnp6tFyy7Oxso1WrVsZf/vIXIzMz07l8/PHHRlRUlLF69WpjxYoVRvPmzY0HHnjAMAzHFRJXXXWV81LBUaNGGf369TOzG5dk9uzZJfaprP6kpaUZ/v7+xqpVq4zc3FzjtttuMx555BEzu1EuXbp0MT744APn4+eff97o2LGjsXHjRuOLL74w6tSpY0yaNMkwDPfsKxdcuXM5f8eff/7ZqFGjhrFt2zYjMzPTaNeunfHyyy+b0p/SnN/P0vbZwsLCUt/jhuE+++35fb3c/rjLe5kSrsa6cJ81DPfdb0t7r+bm5nrMvqqw4yYWL15sAMWWlJQU48knnzRCQkKM8PBwY+zYsc55HQzDMJYsWWIEBgYatWrVMq666irjl19+MbEXl660PpXVn7feesvw8fExwsLCjKioKOPo0aNmlV8u2dnZhq+vr/Hrr7861+Xm5hoPPvigUaNGDaNu3brGs88+a+Tl5Tnb3a2vF35YXO7f8amnnjJ8fX2N4OBgo0OHDkZ2dnZVduOizu9nWfusYZT+HjcM99hvL/ybXm5/3OG9fGFfS9pnDcN999uy3quesq/qrufVwNGjR0lMTCQ2NpZatWqZXc4VK6s/KSkp7Ny5ky5duhAUFGRShVXD3ft6uX/H5ORkDh8+TLdu3Uw/D6Ayab/1TO7YV0/YVxV2RERExKPpBGURERHxaAo7IiIi4tEUdkRERMSjKeyIiIiIR1PYEREREY+msCMiLmX9+vXOGyu6w+uKiOtT2BERERGPprAjIiIiHk1hR0Rc1unTp+nYsSPPPfcckydPZvjw4c62HTt2UKtWLfLz86/oZ3zzzTe0bt2a0NBQRo0ahd1uB2DSpEk88MADPPfcc4SGhhIVFcWmTZuu6GeJiDkUdkTEJRUUFDB48GDat2/PxIkTGTRoECtXruTspO9ff/01AwYMwNvb+7J/xp49exgwYACPPvooP/30Ez/++CMvvfSSs3358uX8/vvvbN26lZtuuolx48Zdcb9EpOop7IiISxo7dizr1q1jxowZALRo0YLatWuzZcsWwDEiM2jQoCv6GZ988gnt2rVj1KhRREdH89BDD7F06VJnu7e3N++88w5RUVGMGDGCgwcPXtHPExFzXP5/iUREKsmBAwf46aefuOGGG3jvvfd49NFHAbjrrrv4+uuvadWqFdu3b6dnz55X9HMOHTrE1q1bCQ0NBSA/P7/IzQxjY2Px8/MDwNfXF91KUMQ9KeyIiMsJCQlh+fLlpKSk0LdvX0aOHElwcDCDBg1i9OjRXH/99fTq1euK76TcsGFD+vfvzyuvvAI4Dp1lZ2c724ODg6/o9UXENegwloi4nJCQECIiIoiJiaFz585MnToVgNatW5ORkcHcuXO56667rvjnDB48mI0bN/Lbb7/h5+fHf//7X0aOHHnFrysirkVhR0Rc2nPPPcerr77KkSNHABg4cCBfffUVvXv3vuLXbtq0KR9//DGPP/44zZo1Y9u2bcyfP/+KX1dEXIvF0EFoERER8WAa2RERt3bgwAFCQ0NLXAYPHmx2eSLiAjSyIyJuLT8/n0OHDpXYFhgYSO3atau4IhFxNQo7IiIi4tF0GEtEREQ8msKOiIiIeDSFHREREfFoCjsiIiLi0RR2RERExKMp7IiIiIhHU9gRERERj6awIyIiIh7t/wOIOC5i7qA0GQAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "q_len = kv_len: prefill:\n",
      "    kv_len      Triton        Torch\n",
      "0    128.0   40.347002   115.275167\n",
      "1    256.0   60.131874    93.988962\n",
      "2    384.0   82.869098   131.452322\n",
      "3    512.0  112.844273   171.279743\n",
      "4    640.0  144.848585   234.813690\n",
      "5    768.0  190.692484   315.986037\n",
      "6    896.0  234.784499   411.332816\n",
      "7   1024.0  281.335682   576.373339\n",
      "8   1152.0  331.157714  1118.415713\n",
      "9   1280.0  380.863696  1393.119216\n",
      "10  1408.0  435.399860  1787.351251\n",
      "11  1536.0  482.876182  2120.450497\n",
      "12  1664.0  545.804322  2432.038784\n",
      "13  1792.0  643.190622  2741.557121\n",
      "14  1920.0  710.895598  3121.858358\n",
      "15  2048.0  796.107590  3535.887241\n"
     ]
    }
   ],
   "source": [
    "device = torch.device('cuda')\n",
    "dtype = torch.float16\n",
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['kv_len'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[128 * i for i in range(1, 16+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['triton', 'torch'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"Triton\",\n",
    "            \"Torch\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"q_len = kv_len: prefill\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'bs': 1, 'num_head': 8, 'rope_head_dim': 32, \n",
    "              'nope_head_dim': 64, 'kv_lora_rank': 256},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(kv_len, provider, bs, num_head, rope_head_dim, nope_head_dim, kv_lora_rank):\n",
    "    q_len = kv_len\n",
    "    v_head_dim = nope_head_dim\n",
    "    scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "    qk_merge = torch.randn(bs,num_head,q_len,kv_lora_rank).to(dtype).to(device)\n",
    "    compress_kv = torch.randn(bs,kv_len,kv_lora_rank).to(dtype).to(device)\n",
    "    v = torch.nn.Linear(kv_lora_rank, num_head*v_head_dim).to(qk_merge.device).to(qk_merge.dtype)\n",
    "    q_rope = torch.randn(bs,num_head,q_len,rope_head_dim).to(dtype).to(device)\n",
    "    k_rope = torch.randn(bs,1,kv_len,rope_head_dim).to(dtype).to(device)\n",
    "\n",
    "    \n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'torch':\n",
    "        ms = triton.testing.do_bench(lambda: torch_mla(qk_merge, compress_kv, v.weight, q_rope, k_rope, scale, v_head_dim))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: triton_mla(qk_merge, compress_kv, v.weight, q_rope, k_rope, scale, v_head_dim))\n",
    "    return ms * 1e3\n",
    "# print('kv_lora_rank=256 nope_head_dim=64 rope_head_dim=32')\n",
    "# print(f'bs=1 num_head=8 q_len=kv_len prefill阶段')\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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8stMnz6dgQMHmlZT3brQsOH19aJcfj8gQMsEiIiIlBSLYVxrKULnt3TpUp5++mmOHTuGr68v8fHxvPjii2zduvWaz7VarQQEBJCWloa/JlwREREpE673+9slRmPEx8cTGRmJr68vAE2aNCExMdHkqkRERMRsLnHqymq1EhYWZr9vsVhwd3cnNTWVoKCgQo/Nzs4m+7LrqK1Wq8PqFBEREcdyiR4dDw8PvP40KYu3tzeZRVxvPWbMGAICAuy30NBQR5UpIiIiDuYSQSc4OJiUlJRC29LT0/H09LziscOGDSMtLc1+S05OdlSZIiIi4mAuceoqIiKCKVOm2O8nJSWRnZ1NcHDwFY/18vK6ovdHREREXJNL9Oi0atUKq9XKtP+buGb06NG0b98ed00oIyIiUq65RI+Oh4cHU6dOJSYmhqFDh+Lm5samTZvMLktERERM5hJBByAqKopDhw4RFxdHZGQklStXNrskERERMZnLBB2A6tWr06VLF7PLEBERESfhEmN0RERERIqioCMiIiIuS0FHREREXJaCjoiIiLgsBR0RERFxWS511dXNMAwD0OKeIiIiZUnB93bB9/jVlPugk56eDqDFPUVERMqg9PR0AgICrtpuMa4VhVxcfn4+J06coFKlSlgsFrPLKVVWq5XQ0FCSk5Px9/c3u5xyT8fD+eiYOB8dE+fiTMfDMAzS09OpWbMmbm5XH4lT7nt03NzcqF27ttllOJS/v7/p/0DlEh0P56Nj4nx0TJyLsxyP4npyCmgwsoiIiLgsBR0RERFxWQo65YiXlxcjR47Ey8vL7FIEHQ9npGPifHRMnEtZPB7lfjCyiIiIuC716IiIiIjLUtARERERl6WgIyIiIi5LQceFLFu2jPr16+Ph4cHdd9/Nr7/+CkBCQgIREREEBQUxdOjQQtNlb968mYYNGxISEsK4cePMKt3lderUienTpwPFv+eLFi2ibt261KxZk3nz5plQafnwz3/+k27dutnv63fEPFOnTiU0NBRfX18efvhhfvvtN0DHxNHOnDlDWFgYhw8ftm+72WPgdJ9jhriEgwcPGkFBQcaCBQuMU6dOGb179zYefPBBIysry6hXr57x17/+1Th48KDRuXNn44svvjAMwzBOnz5t+Pv7G2+//baxf/9+49577zU2bNhg8p64ntmzZxuAMW3atGLf8z179hienp7GlClTjN27dxu33367sXfvXpOrdz3x8fGGn5+fcejQIcMwDP2OmOjgwYNGaGioERcXZxw5csR4+umnjZYtW+qYOFhKSopx//33G4CRlJRkGMbN/1444+eYgo6LWLFihfHpp5/a72/YsMHw8fExlixZYgQFBRnnz583DMMwdu3aZbRo0cIwDMMYP3688Ze//MXIz883DMMwli5davTr18/xxbuws2fPGtWqVTMaNGhgTJs2rdj3fMiQIUbHjh3tz50wYYLx5ptvmlK3q8rLyzPuv/9+Y/jw4fZt+h0xz8KFC43evXvb72/dutWoUaOGjomDtWvXzpg4cWKhoHOzx8AZP8d06spFdO3aleeee85+f9++fYSHhxMfH09kZCS+vr4ANGnShMTERADi4+Np06aNfY2v5s2bExcX5/jiXdhrr73GY489RmRkJFD8ex4fH0/btm3tz9XxKHmffPIJe/bsoV69eixfvpyLFy/qd8REjRo1YsOGDezatYu0tDQ+/vhjHnnkER0TB5syZQovv/xyoW03ewyc8XNMQccFXbx4kf/85z88//zzWK1WwsLC7G0WiwV3d3dSU1OvaPP39+fEiRNmlOySNm7cyPr16xk7dqx9W3HvuY5H6crIyGDkyJHUr1+fI0eOMH78eB566CH9jpioUaNG9OrVi3vuuYfAwEC2b9/OBx98oGPiYJe/nwVu9hg44/FR0HFBI0eOpGLFigwaNAgPD48rZrD09vYmMzPziraC7XLrsrKy+Otf/8rkyZOpVKmSfXtx77mOR+n66quvOH/+PBs3buTtt99m7dq1pKen88UXX+h3xCQ//fQTK1as4IcffuDcuXPExMTQuXNnfW45gZs9Bs54fBR0XMyGDRuYNGkSc+fOpUKFCgQHB5OSklLoMenp6Xh6el7RVrBdbt0777xDREQEXbp0KbS9uPdcx6N0HTt2jMjISEJCQgDbB3KTJk04d+6cfkdMMm/ePPr27cv9999PQEAA7777LocOHdLnlhO42WPgjMdHQceFJCUlERMTw6RJk2jUqBEAERERbN++vdBjsrOzCQ4OvqJt586d1KpVy+F1u6K5c+eybNkyAgMDCQwMZO7cuQwePJgZM2Zc9T3X8ShdtWvX5sKFC4W2HTlyhAkTJuh3xCT5+fmcPn3afj89Pd3eY6BjYq6b/e5wyuNj6lBoKTGZmZlGo0aNjGeffdZIT0+33y5evGhUqVLFflngoEGDjK5duxqGYbuk0Nvb21i7dq1x8eJFo1OnTsZLL71k5m64jOTkZCMpKcl+i46ONt5///1i3/Ndu3YZFStWNHbv3m2kp6cbd999t/HBBx+YvCeu48yZM4a/v78xefJkIzk52Zg4caLh7e1tHD16VL8jJlm4cKHh6+trjBs3zpgzZ47Rpk0bo27duvrcMgmXXXWVk5NzU8fAGT/HFHRcxNKlSw3giltSUpKxbNkyw9fX16hcubJRpUoV45dffrE/b/LkyUaFChWMoKAgIywszDh16pSJe+G6BgwYYEybNs0wjOLf8zfeeMPw9PQ0/P39jfvuu8/IzMw0qWLXtHXrViMyMtLw8fEx6tevbyxfvtwwDEO/IybJz883Ro0aZdSpU8eoUKGCcc899xj//e9/DcPQMTHD5UHHMG7+GDjb55hWLy8nTp06RVxcHJGRkVSuXLlQW1JSEnv37qVly5b4+fmZVGH5Utx7npiYyPHjx2ndurXp57bLE/2OOB8dE/Pd7DFwps8xBR0RERFxWRqMLCIiIi5LQUdERERcloKOiIiIuCwFHREREXFZCjoiIiLishR0RMSpbNq0iXr16pWZ1xUR56agIyIiIi5LQUdERERcloKOiDit8+fP06xZM0aNGsXo0aPp37+/vS0hIYHKlSuTm5t7Sz9j9erVNG7cmMDAQAYNGkR2djYAb731Fk899RSjRo0iMDCQsLAwtm3bdks/S0QcT0FHRJxSXl4effv25d5772XEiBFER0fz7bffUjCZ+zfffEP37t3x8PC46Z9x8OBBunfvzpAhQ/j555/56aefeP/99+3tq1at4rfffmPnzp20aNGCYcOG3fJ+iYhjKeiIiFN6+eWX2bhxIxMmTACgQYMGVK1alR07dgC2npjo6Ohb+hkLFizg7rvvZtCgQYSHhzN48GCWL19ub/fw8ODTTz8lLCyM2NhYkpOTb+nniYjj3fx/hURESsnRo0f5+eefuf/++5kyZQpDhgwBoFevXnzzzTc0atSIPXv28Mgjj9zSzzl27Bg7d+4kMDAQgNzc3EKLE0ZGRuLl5QWAp6cnWhpQpOxR0BERpxMQEMCqVatISkqiS5cuDBw4EH9/f6Kjo3nuuee455576NChwy2vily7dm26devGf/7zH8B2uiwzM9Pe7u/vf0uvLyLm06krEXE6AQEBhISEEBERwQMPPMDYsWMBaNy4MWlpacyZM4devXrd8s/p27cvW7Zs4cCBA3h5efG///u/DBw48JZfV0Sch4KOiDi1UaNGMXHiRE6ePAlAjx49WLlyJR07drzl177tttuYOXMmr776Krfffju7d+9m3rx5t/y6IuI8LIZOOouIiIiLUo+OiJRpR48eJTAwsMhb3759zS5PREymHh0RKdNyc3M5duxYkW2+vr5UrVrVwRWJiDNR0BERERGXpVNXIiIi4rIUdERERMRlKeiIiIiIy1LQEREREZeloCMiIiIuS0FHREREXJaCjoiIiLgsBR0RERFxWf8f2Sxc0oNqnZAAAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "q_len = kv_len: prefill:\n",
      "   kv_len       Triton        Torch\n",
      "0   128.0    75.983420   164.442390\n",
      "1   256.0   155.660048   309.486747\n",
      "2   384.0   299.459577   775.281012\n",
      "3   512.0   467.520714  1665.383935\n",
      "4   640.0   689.472795  2690.070152\n",
      "5   768.0   963.633478  3747.799635\n",
      "6   896.0  1273.005009  5050.609112\n",
      "7  1024.0  1629.769921  6653.682709\n"
     ]
    }
   ],
   "source": [
    "device = torch.device('cuda')\n",
    "dtype = torch.float16\n",
    "\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['kv_len'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[128 * i for i in range(1, 8+1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['triton', 'torch'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"Triton\",\n",
    "            \"Torch\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"q_len = kv_len: prefill\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'bs': 2, 'num_head': 32, 'rope_head_dim': 32, \n",
    "              'nope_head_dim': 64, 'kv_lora_rank': 256},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(kv_len, provider, bs, num_head, rope_head_dim, nope_head_dim, kv_lora_rank):\n",
    "    q_len = kv_len\n",
    "    v_head_dim = nope_head_dim\n",
    "    scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "    qk_merge = torch.randn(bs,num_head,q_len,kv_lora_rank).to(dtype).to(device)\n",
    "    compress_kv = torch.randn(bs,kv_len,kv_lora_rank).to(dtype).to(device)\n",
    "    v = torch.nn.Linear(kv_lora_rank, num_head*v_head_dim).to(qk_merge.device).to(qk_merge.dtype)\n",
    "    q_rope = torch.randn(bs,num_head,q_len,rope_head_dim).to(dtype).to(device)\n",
    "    k_rope = torch.randn(bs,1,kv_len,rope_head_dim).to(dtype).to(device)\n",
    "\n",
    "    \n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    if provider == 'torch':\n",
    "        ms = triton.testing.do_bench(lambda: torch_mla(qk_merge, compress_kv, v.weight, q_rope, k_rope, scale, v_head_dim))\n",
    "    if provider == 'triton':\n",
    "        ms = triton.testing.do_bench(lambda: triton_mla(qk_merge, compress_kv, v.weight, q_rope, k_rope, scale, v_head_dim))\n",
    "    return ms * 1e3\n",
    "\n",
    "benchmark.run(show_plots=True, print_data=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 误差测试"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "False\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "device = torch.device('cuda')\n",
    "dtype = torch.bfloat16\n",
    "bs,num_head,q_len,kv_len, rope_head_dim,nope_head_dim=8,8,256,256, 32, 64\n",
    "kv_lora_rank = 256\n",
    "scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "qk_merge = torch.randn(bs,num_head,q_len,kv_lora_rank).to(dtype).to(device)\n",
    "compress_kv = torch.randn(bs,kv_len,kv_lora_rank).to(dtype).to(device)\n",
    "v = torch.nn.Linear(kv_lora_rank, num_head*nope_head_dim).to(qk_merge.device).to(qk_merge.dtype)\n",
    "q_rope = torch.randn(bs,num_head,q_len,rope_head_dim).to(dtype).to(device)\n",
    "k_rope = torch.randn(bs,1,kv_len,rope_head_dim).to(dtype).to(device)\n",
    "mask = torch.ones(bs, kv_len, device=device, dtype=dtype)\n",
    "padding = mask.shape[-1] - mask.sum(-1)\n",
    "a = triton_mla(qk_merge, compress_kv, v.weight, q_rope, k_rope, scale, nope_head_dim, padding)\n",
    "b = torch_mla(qk_merge, compress_kv, v.weight, q_rope, k_rope, scale, nope_head_dim, mask, golden=True).to(a.dtype)\n",
    "print(torch.allclose(a, b, 0.001, 0.001))\n",
    "print(torch.allclose(a, b, 0.005, 0.005))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "ename": "RuntimeError",
     "evalue": "Half did not match Float",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRuntimeError\u001b[0m                              Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[4], line 15\u001b[0m\n\u001b[1;32m     13\u001b[0m a \u001b[38;5;241m=\u001b[39m triton_mla(qk_merge, compress_kv, v\u001b[38;5;241m.\u001b[39mweight, q_rope, k_rope, scale, nope_head_dim, padding)\n\u001b[1;32m     14\u001b[0m b \u001b[38;5;241m=\u001b[39m torch_mla(qk_merge, compress_kv, v\u001b[38;5;241m.\u001b[39mweight, q_rope, k_rope, scale, nope_head_dim, mask, golden\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[0;32m---> 15\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mtorch\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mallclose\u001b[49m\u001b[43m(\u001b[49m\u001b[43ma\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mb\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0.001\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m0.001\u001b[39;49m\u001b[43m)\u001b[49m)\n\u001b[1;32m     16\u001b[0m \u001b[38;5;28mprint\u001b[39m(torch\u001b[38;5;241m.\u001b[39mallclose(a, b, \u001b[38;5;241m0.005\u001b[39m, \u001b[38;5;241m0.005\u001b[39m))\n",
      "\u001b[0;31mRuntimeError\u001b[0m: Half did not match Float"
     ]
    }
   ],
   "source": [
    "device = torch.device('cuda')\n",
    "dtype = torch.float16\n",
    "bs,num_head,q_len,kv_len, rope_head_dim,nope_head_dim=8,8,256,256, 32, 64\n",
    "kv_lora_rank = 256\n",
    "scale = (rope_head_dim + nope_head_dim) ** (-0.5)\n",
    "qk_merge = torch.randn(bs,num_head,q_len,kv_lora_rank).to(dtype).to(device)\n",
    "compress_kv = torch.randn(bs,kv_len,kv_lora_rank).to(dtype).to(device)\n",
    "v = torch.nn.Linear(kv_lora_rank, num_head*nope_head_dim).to(qk_merge.device).to(qk_merge.dtype)\n",
    "q_rope = torch.randn(bs,num_head,q_len,rope_head_dim).to(dtype).to(device)\n",
    "k_rope = torch.randn(bs,1,kv_len,rope_head_dim).to(dtype).to(device)\n",
    "mask = torch.ones(bs, kv_len, device=device, dtype=dtype)\n",
    "padding = mask.shape[-1] - mask.sum(-1)\n",
    "a = triton_mla(qk_merge, compress_kv, v.weight, q_rope, k_rope, scale, nope_head_dim, padding)\n",
    "b = torch_mla(qk_merge, compress_kv, v.weight, q_rope, k_rope, scale, nope_head_dim, mask, golden=True).to(a.dtype)\n",
    "print(torch.allclose(a, b, 0.001, 0.001))\n",
    "print(torch.allclose(a, b, 0.005, 0.005))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# v_weight先乘还是后乘，复杂度对比"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import matplotlib.pyplot as plt\n",
    "from functools import partial\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def func(N, v_head_dim, kv_lora_rank, BLOCK_M, BLOCK_N):\n",
    "    steps = math.ceil(N/BLOCK_N)\n",
    "\n",
    "    #1 先乘v_weight\n",
    "    num_flops1 = (BLOCK_M * BLOCK_N * v_head_dim + BLOCK_N * kv_lora_rank * v_head_dim) * steps\n",
    "\n",
    "    num_flops2 = (BLOCK_M * BLOCK_N * kv_lora_rank) * steps + BLOCK_M * kv_lora_rank * v_head_dim\n",
    "\n",
    "    return num_flops1*2, num_flops2*2\n",
    "new_func = partial(func, v_head_dim=64, kv_lora_rank=256, BLOCK_M=16, BLOCK_N=128)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "end = 33\n",
    "data = np.array([new_func(i*128) for i in range(1, end+1)]).T\n",
    "plt.plot(data[0])\n",
    "plt.plot(data[1])\n",
    "plt.xticks([i for i in range(0, end, 4)], [i*128 for i in range(0, end, 4)])\n",
    "plt.legend(['pre * ', 'post *'])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.7777777777777777"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a,b=new_func(512)\n",
    "a/b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "torch",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.14"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
